#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>

#define BUFLEN 128
int main(int argc, char **argv)
{
	int serverFd, clientFd;
	int len, ret, rlen;
	char buf[BUFLEN];
	struct sockaddr_in serverAddr, clientAddr;
	
	if (argc != 2)
	{
		printf("Usage:%s port\n", argv[0]);
		return 0;
	}
	short port;
	port = atoi(argv[1]);
	len = sizeof(serverAddr);

	serverFd = socket(AF_INET, SOCK_STREAM, 0);
	
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(port);
	serverAddr.sin_addr.s_addr = 0;
	int b_reuse = 1;
	setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
	ret = bind(serverFd, (struct sockaddr *)&serverAddr, len);
	if (ret < 0)
	{
		perror("bind");
		return -1;
	}
	
	ret = listen(serverFd, 5);
	if (ret < 0)
	{
		perror("listen");
		return -1;
	}
	
	int epfd, epct, i;
	struct epoll_event event;//定义epoll事件
	struct epoll_event events[20];//定义epoll事件集合
	memset(events, 0, 20 * sizeof(struct epoll_event));
	epfd = epoll_create(1);//创建epfd
	
	event.data.fd = serverFd;//定义事件的fd
	event.events = EPOLLIN;//填充事件类型（读事件）
	epoll_ctl(epfd, EPOLL_CTL_ADD, serverFd, &event);//给epfd添加要监听的事件
	while (1)
	{
		epct = epoll_wait(epfd, events, 20, -1);//等待事件到来，阻塞模式，返回事件的个数
		for (i = 0; i < epct; i++)
		{
			if (events[i].data.fd == serverFd)//如果事件的fd是监听事件serverFd,则表示有连接接入
			{
				clientFd = accept(events[i].data.fd, (struct sockaddr *)&clientAddr, &sizeof(clientAddr));
				printf("new fd = %d ip %s\n", clientFd, inet_ntoa(clientAddr.sin_addr));
				event.data.fd = clientFd;
				event.events = EPOLLIN | EPOLLET;
				epoll_ctl(epfd, EPOLL_CTL_ADD, clientFd, &event);
			}
			else//如果不是serverFd，那一定是数据到来事件(因为经过上一次循环clientFd加入了事件红黑树)，调用读数据
			{
				memset(buf, 0, BUFLEN);
				rlen = read(events[i].data.fd, buf, BUFLEN);
				if (rlen <= 0)
				{
					printf("fd %d disconnected\n", events[i].data.fd);
					close(events[i].data.fd);//关闭连接
					epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &event);
					continue;
				}
				printf("Read:%s\n", buf);
			}
		}
	}
	return 0;
}